#pragma once

#include <iostream>
#include <string>
#include <assert.h>
#include <vector>
#include <mutex>
#include<algorithm>
#include <jsoncpp/json/json.h>
#include "oj_model.hpp"
#include "oj_view.hpp"
#include "../comm/httplib.h"

namespace ns_Control
{
   using namespace std;
   using namespace ns_Model;
   using namespace ns_log;
   using namespace ns_util;
   using namespace ns_View;
   using namespace httplib;

   //提供服务的主机
   class Machine
   {
   public:
      string ip;     //编译服务的ip
      int port;      //编译服务的端口
      uint64_t load; //编译服务的负载
      mutex *mtx;    //C++库中的锁不允许拷贝，所以用指针

      Machine() : ip(""), port(0), load(0), mtx(nullptr)
      {
      }
      ~Machine()
      {
      }
      void ResetLoad()
      {
         if (mtx)
            mtx->lock();
         load = 0;
         if (mtx)
            mtx->unlock();
      }
      int Addload()
      {
         if (mtx)
            mtx->lock();
         ++load;
         if (mtx)
            mtx->unlock();
      }

      int Decload()
      {
         if (mtx)
            mtx->lock();
         --load;
         if (mtx)
            mtx->unlock();
      }

      int Getload()
      {
         uint64_t _load = 0;
         if (mtx)
            mtx->lock();
         _load = load;
         if (mtx)
            mtx->unlock();
         return _load;
      }
   };

   const string server_machine = "./conf/server_machine.conf";

   //负载均衡模块
   class LoadBlance
   {
   private:
      //所有的主机
      //每一台主机都有自己的下标，
      vector<Machine> machine;
      //所有在线的主机
      vector<int> online;
      //所有离线的主机
      vector<int> offline;
      mutex mtx;

   public:
      LoadBlance()
      {
         assert(LoadConf(server_machine));
         LOG(INFO) << "加载: " << server_machine << " 成功" << endl;
      }
      ~LoadBlance()
      {
      }

      //把所有主机加载，下标在online中
      bool LoadConf(const string &machine_conf)
      {
         ifstream in(machine_conf);
         if (!in.is_open())
         {
            LOG(FATAL) << "加载: " << machine_conf << " 失败" << endl;
            return false;
         }

         string line;
         while (getline(in, line))
         {
            vector<string> tokens;
            StringUtil::SplitString(line, &tokens, ":");
            // cout<<tokens[0]<<endl;
            // cout<<tokens[1]<<endl;
            Machine m;
            m.ip = tokens[0];
            m.load = 0;
            m.mtx = new mutex();
            m.port = atoi(tokens[1].c_str());
            online.push_back(machine.size());
            // cout<<machine.size()<<endl;
            machine.push_back(m);
         }
         in.close();
         return true;
      }
      //选择负载最小的服务器
      bool SmartChoice(int *id, Machine **m)
      {
         mtx.lock();
         int online_num = online.size();
         if (online_num == 0)
         {
            mtx.unlock();
            LOG(FATAL) << "所有服务器都挂掉了" << endl;
            return false;
         };
         *m = &machine[online[0]];
         *id = online[0];
         uint64_t min_load = machine[online[0]].Getload();
         for (int i = 0; i < online_num; ++i)
         {
            uint64_t temp_load = machine[online[i]].Getload();
            if (min_load > temp_load)
            {
               min_load = temp_load;
               *id = online[i];
               // *id = i;错误的代码
               *m = &machine[online[i]];
            }
         }
         mtx.unlock();
         return true;
      }

      void OfflineMachine(int id)
      {
         mtx.lock();
         for (auto iter = online.begin(); iter != online.end(); ++iter)
         {
            if (*iter == id)
            {
               machine[id].ResetLoad();
               online.erase(iter);
               offline.push_back(id);
               break; //有这个，就可以暂时不用考虑迭代器失效的情况
            }
         }
         mtx.unlock(); //加锁的原因是因为当我们处理离线的主机时，防止此时刚好请求在线主机
      }
      void OnlineMachine()
      {
         //当所有主机离线的时候，我们统一上线
         mtx.lock();
         online.insert(online.begin(),offline.begin(),offline.end());
         offline.erase(offline.begin(),offline.end());
         mtx.unlock();
         LOG(INGO)<<"所有的主机已经上线"<<endl;
      }
      // for test
      void ShowMachine()
      {
         //以你为当你在输出时，可能有人正在访问
         mtx.lock();
         cout << "当前在线主机列表" << endl;
         for (auto &iter : online)
         {
            cout << iter << " ";
         }
         cout << endl;

         cout << "当前离线主机列表" << endl;
         for (auto &iter : offline)
         {
            cout << iter << " ";
         }
         cout << endl;
         mtx.unlock();
      }
   };

   class Control
   {
   private:
      Model mod;
      LoadBlance load_blance; //负载均衡器
      View view_;

   public:

      void RecoryMachine()
      {
         load_blance.OnlineMachine();
      }

      bool AllQuestion(string *html)
      {
         //获取所有的题目数据，并且构建一个网页返回
         vector<Question> q;
         if (mod.GetAllQuestions(&q))
         { 
            sort(q.begin(),q.end(),[](const struct Question& q1,const struct Question& q2){
               return atoi(q1.num.c_str())<atoi(q2.num.c_str());
            }) ;
            view_.AllExpandHtml(q, html);
         }
         else
         {
            *html = "获取题库失败，形成题目列表失败";
         }
      }

      bool OneQuestion(const string num, string *html)
      {
         Question q;
         if (mod.GetOneQuestions(num, &q))
         {
            view_.OneExpandHtml(q, html);
         }
         else
         {
            *html = "没有对应的题号，" + num;
         }
      }
      bool Judge(const string &num, const string in_json, string *out_json)
      {
         // 1.根据题号，直接得到相对应的题目细节
         struct Question q;
         mod.GetOneQuestions(num, &q);
         // in_json:{"code":"#inclde";"input":"";"cpu_limit","mem_
         // 2.进行反序列化，得到用户提交的代码、input、cpu、mem
         Json::Reader reader;
         Json::Value in_value;
         reader.parse(in_json, in_value);
         string code = in_value["code"].asString();
         // 3.重新拼接用户+测试代码，形成新的代码
         Json::Value compile_code;
         compile_code["input"] = in_value["input"].asString();
         compile_code["code"] = code + q.tail;
         compile_code["cpu_limit"] = q.cpu_limit;
         compile_code["mem_limit"] = q.mem_limit;
         Json::FastWriter writer;
         string compile_string = writer.write(compile_code);
         // 4.选择负载最低的主机
         //一直进行选择，直到主机可以用，否则就是全部挂掉
         while (true)
         {
            int id = 0;
            Machine *m = nullptr;
            if (!load_blance.SmartChoice(&id, &m))
            {
               break;
            }
            // 5.发起http请求，得到结果
            Client cli(m->ip, m->port); //向那台主机的那个端口发起请求
             m->Addload();
            LOG(INFO) << "选择主机成功，主机id: " << id << "详情：" << m->ip << ":" << m->port <<"当前主机的负载"<<m->Getload()<<endl;
            if (auto res = cli.Post("/compile_and_run", compile_string, "application/json;charset=utf-8"))
            {
               ///cout << res->status << endl;
               // 6.将得到的结果赋值给out_json；只有当http响应的状态码是200(OK)时，说明响应才成功
               if (res->status == 200)
               {
                  *out_json = res->body;
                  m->Decload();
                  LOG(INFO) << "请求编译和运行服务成功" << endl;
                  break;
               }
               m->Decload();
               cout<<"abcd"<<endl;
               //说明访问到主机，但是响应是不正确，让其继续请求
            }
            else
            {
               //请求失败
               LOG(ERROR) << "当前请求的主机id：" << id << "详情: " << m->ip << ":" << m->port << "可能挂掉了" << endl;
               m->Decload(); //可以没有必要，因为下一行的offline会把负载清零
               load_blance.OfflineMachine(id);
               load_blance.ShowMachine();
            }
         }
      }
   };

}
